using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Sustainalytics.Entities;
using Sustainalytics.Synchronization.Validation.Internals;
using Sustainalytics.Synchronization.Validation.ValidationRules.Common;

namespace Sustainalytics.Synchronization.Validation.Restore
{
    public abstract class AbstractProfileRestoreHandler<T> : IProfileRestoreHandler
        where T : IEntity
    {
        protected readonly IMongoCollection<T> ReferenceDataCollection;

        protected readonly IMongoCollection<T> ValidatedDataCollection;

        protected AbstractProfileRestoreHandler(ValidationMongoCollection<T> collections)
        {
            this.ValidatedDataCollection = collections.CurrentCollection;
            this.ReferenceDataCollection = collections.OldCollection;
        }

        protected BusinessResearchProduct ResearchProduct { get; set; }

        public abstract Task ForEachElementsAsync(InvalidCompaniesList invalidCompanies);

        public virtual Task<IEnumerable<CompanyIdResearchProductTuple>> GetInvalidNewEntitiesAsync(
            ICollection<T> foundEntities,
            ICollection<Guid> invalidCompanies,
            Func<T, Guid> companyKeyFunc)
        {
            if (foundEntities == null)
            {
                throw new ArgumentNullException(nameof(foundEntities));
            }

            if (!foundEntities.Any())
            {
                return Task.FromResult(invalidCompanies.Select(x => new CompanyIdResearchProductTuple(x, ResearchProduct)));
            }

            var result = from invalidCompany in invalidCompanies
                         let companies = from entity in foundEntities select companyKeyFunc(entity)
                         where !companies.Contains(invalidCompany)
                         select new CompanyIdResearchProductTuple(invalidCompany, ResearchProduct);
            return Task.FromResult(result);
        }


        public virtual Task<List<T>> GetReferenceElementsAsync(IEnumerable<Guid> companyIds)
        {
            return
                ReferenceDataCollection.Find(GetEntityFilter(companyIds))
                    .ToListAsync();
        }

        protected abstract FilterDefinition<T> GetEntityFilter(IEnumerable<Guid> companyIds);
    }
}
